#include "instructions/extended/extended.h"
#include "instructions/base/branch_logic.h"
#include "instructions/math/math_instructions.h"
#include "instructions/loads/aload.h"
#include "instructions/loads/dload.h"
#include "instructions/loads/fload.h"
#include "instructions/loads/iload.h"
#include "instructions/loads/lload.h"
#include "instructions/stores/dstore.h"
#include "instructions/stores/fstore.h"
#include "instructions/stores/istore.h"
#include "instructions/stores/lstore.h"
#include "instructions/stores/astore.h"

namespace jvm {
namespace instructions {

// IFNULL implementation
void IFNULL::execute(std::shared_ptr<rtda::Frame> frame) {
    auto ref = frame->getOperandStack()->popRef();
    if (ref == nullptr) {
        branch(frame, getOffset());
    }
}

// IFNONNULL implementation
void IFNONNULL::execute(std::shared_ptr<rtda::Frame> frame) {
    auto ref = frame->getOperandStack()->popRef();
    if (ref != nullptr) {
        branch(frame, getOffset());
    }
}

// WIDE implementation
void WIDE::fetchOperands(std::shared_ptr<BytecodeReader> reader) {
    uint8_t opcode = reader->readUint8();
    switch (opcode) {
        case 0x15: { // ILOAD
            auto inst = std::make_shared<ILOAD>();
            inst->setIndex(reader->readUint16());
            _modifiedInstruction = inst;
            break;
        }
        case 0x16: { // LLOAD
            auto inst = std::make_shared<LLOAD>();
            inst->setIndex(reader->readUint16());
            _modifiedInstruction = inst;
            break;
        }
        case 0x17: { // FLOAD
            auto inst = std::make_shared<FLOAD>();
            inst->setIndex(reader->readUint16());
            _modifiedInstruction = inst;
            break;
        }
        case 0x18: { // DLOAD
            auto inst = std::make_shared<DLOAD>();
            inst->setIndex(reader->readUint16());
            _modifiedInstruction = inst;
            break;
        }
        case 0x19: { // ALOAD
            auto inst = std::make_shared<ALOAD>();
            inst->setIndex(reader->readUint16());
            _modifiedInstruction = inst;
            break;
        }
        case 0x36: { // ISTORE
            auto inst = std::make_shared<ISTORE>();
            inst->setIndex(reader->readUint16());
            _modifiedInstruction = inst;
            break;
        }
        case 0x37: { // LSTORE
            auto inst = std::make_shared<LSTORE>();
            inst->setIndex(reader->readUint16());
            _modifiedInstruction = inst;
            break;
        }
        case 0x38: { // FSTORE
            auto inst = std::make_shared<FSTORE>();
            inst->setIndex(reader->readUint16());
            _modifiedInstruction = inst;
            break;
        }
        case 0x39: { // DSTORE
            auto inst = std::make_shared<DSTORE>();
            inst->setIndex(reader->readUint16());
            _modifiedInstruction = inst;
            break;
        }
        case 0x3a: { // ASTORE
            auto inst = std::make_shared<ASTORE>();
            inst->setIndex(reader->readUint16());
            _modifiedInstruction = inst;
            break;
        }
        case 0x84: { // IINC
            auto inst = std::make_shared<IINC>();
            inst->setIndex(reader->readUint16());
            inst->setConst(reader->readInt16());
            _modifiedInstruction = inst;
            break;
        }
        case 0xa9: // ret
            throw std::runtime_error("Unsupported opcode: 0xa9!");
        default:
            throw std::runtime_error("Invalid opcode for WIDE instruction");
    }
}

void WIDE::execute(std::shared_ptr<rtda::Frame> frame) {
    _modifiedInstruction->execute(frame);
}

// GOTO_W implementation
void GOTO_W::fetchOperands(std::shared_ptr<BytecodeReader> reader) {
    _offset = reader->readInt32();
}

void GOTO_W::execute(std::shared_ptr<rtda::Frame> frame) {
    branch(frame, _offset);
}

} // namespace instructions
} // namespace jvm